'''

    Base classes for KeyGen object (input algos)
    
    The KeyGen is responsible for key generation.
    It contains a ControlMap object configuring the input.
    (See SHIK.controlmap.ControlMap() class)
    
    The KeyGen receives PushControl() instances when some are 
    pressed or released, use the ControlMap to convert them to 
    Events() instances and then use an event->key map find the
    key to issue.
    The key issued will be in one of those forms:
        None    : No key, does nothing
        "a"     : The lower case letter "a"
        "+a"    : Shift and the lower case letter "a"
        "^a"    : Control and the lower case letter "a"
        "^+a"   : Shift and Control and the lower case letter "a"
        "{ENTER}"  : The special key Enter
        "{TAB}"    : The special key Tab
        "{DELETE}" : The special key Delete
        ...
            
'''


class Event(object):
    '''
    Event instances are generated by ControlMap instances and 
    used by KeyGen to store states and generate appropriate keys.
    '''
    def __init__(self, name):
        self._name = name
    
    @property
    def name(self):
        '''
        The name of the event.
        '''
        if self._name is None:
            return 'UnknownEvent'
        return self._name
    
    def __repr__(self):
        if self.name is None:
            return __name__+'.'+self.__class__.__name__+'.Unknown'
        return __name__+'.'+self.__class__.__name__+'('+repr(self.name)+')'
    
    def __str__(self):
        return str(self.name)
    
    def __eq__(self, other):
        return type(self) == type(other) and self.name == other.name
    
    def __hash__(self):
        return self.name.__hash__()
        
# Add the Unknown instance to the Event class
Event.Unknown = Event(None)


class KeyGen(object):
    @classmethod
    def managed_events(cls):
        '''
        Returns a tuple of Events used by this KeyGen.
        This is used by the Device to generate the ControlMap.
        
        Subclasses must implement this.
        '''
        raise NotImplementedError
            
    def __init__(self, control_map=None):
        '''
        Creates the KeyGen instance using control_map.
        
        If control_map is not given, a call to set_control_map() must
        be done before a call to pressed()
        
        '''
        object.__init__(self)
        if control_map is not None:
            self.set_control_map(control_map)
        
        # protected attributes to store KMap states:
        
        self._events = []       # current active events
        self._applied = False   # process on next key release?
        self._cap_next = False  # next is uppercase?
        self._caps_lock = False # all is uppercase ?
        
        # protected attributes to store events converter:
        
        self._events_map = {}       # default events map
        self._next_events_map = {}  # events map override or None
        
    def set_control_map(self, control_map):
        '''
        Changes the KeyGen control_map.
        Subclasses overriding this must call the parent
        implementation.
        '''
        self._control_map = control_map
    
    def set_one_shot_events_map(self, events_map):
        '''
        This is used to change the events map to use for
        next get_result call.
        See also set_default_events_map()
        '''
        self._next_events_map = events_map
    
    def set_default_events_map(self, events_map):
        '''
        This changes the default events map, i.e. the
        one used when no one shot map has been set.
        
        The events_map must be a dict like:
            {(Event,Event,Event): data}
        where data can be any of the types returned by
        the get_result method or a callable with no argument
        returning any of the types returned by the get_result 
        method.
        '''
        self._events_map = events_map
    
    def get_events(self):
        '''
        Returns a copy of the current active events.
        (The one pressed and not yet released.)
        
        Default behavior is to return a tuple and thus use
        press order to find out the result.
        
        Subclasses may override this (and/or events_eq) to
        modify this policy.
        When doing so, you must start by calling the base 
        implementation and modify the result before returning 
        the modified version.
        '''
        return list(self._events)
    
    @classmethod
    def events_eq(cls, eventsa, eventsb):
        '''
        Compares the two given events and returns
        True if they can be considered equal.
        
        This is called to find a key in the events_map when
        requesting the result of current events.
        
        You may subclass to change the meaning of the event
        order and/or the meaning of the unknown event etc...
        
        '''
        return eventsa == eventsb
            
    def get_result(self):
        '''
        Returns the result of the current active events.
        The returned value may be one of:
            - a string like "a", "^a", "+a" or "{Enter}"
            - None (meaning 'do nothing')
        
        This is not to be overriden, subclasses me use events_map
        with calls to set_default_events_map and/or calls to
        set_one_shot_events_map to define the result.
        '''
        self._applied = True
        
        if Event.Unknown in self._events:
            # Do nothing if we have some bad event
            return None
        
        #print "### GET RESUTL OF:", self._events
        if self._next_events_map is not None:
            events_map = self._next_events_map
            self._next_events_map = None
        else:
            events_map = self._events_map
        
        #print "in:\n"+`events_map`
        events = self.get_events()
        result = None
        events_eq = self.events_eq # for fast access
        for k, v in events_map.iteritems():
            if events_eq( k, events):
                result = v
                break
        #print "->", result
        
        if callable(result):
            result = result()
        
        result = self.apply_cap(result)        

        #print "->", `result`
        return result
    
    def get_futur_results(self):
        '''
        Return a dict like {event: result} showing
        what we would emit if the next release if the key
        event.
        '''
        keys = self.managed_events()
        if Event.Unknown in self._events:
            # Do nothing if we have some bad event
            return dict([ (e,None) for e in keys ])
        
        #print "### GET RESUTL OF:", self._events
        if self._next_events_map is not None:
            events_map = self._next_events_map
        else:
            events_map = self._events_map
        
        events = self.get_events()
        events_eq = self.events_eq # for fast access
        results = {}
        for e in keys:
            result = None
            if e not in self._events:
                futur_events = list(events)
                futur_events.append(e)
                for k, v in events_map.iteritems():
                    if events_eq(k, futur_events):
                        result = v
                        break
            results[e] = self.apply_cap(result,update_state=False)
        return results
    
    def pressed(self, push_control):
        '''
        This must be called with the appropriate PushControl
        instance when something has been pressed.
        
        See SHIK.device._abstract.PushControl and 
        SHIK.device._abstract.Device
        
        It will return True if the KeyGen will react to
        this press.
        '''
        self._applied = False
        #print "KeyGen pressed", repr(push_control)
        event = self._control_map.convert_push_control(push_control)
        if event in (None, Event.Unknown):
            return False
        if event not in self._events:
            self._events.append(event)
            #print '  PRESS', self._events
            return True
        return False
    
    def released(self, push_control):
        '''
        This must be called with the appropriate PushControl
        instance when something has been released.
        
        See SHIK.device._abstract.PushControl and 
        SHIK.device._abstract.Device
        
        It will return the data to emit if the KeyGen
        reacts to this event or False.
        Not that the data to emit may be None.
        '''
        event = self._control_map.convert_push_control(push_control)
        
        ret = None
        if not self._applied:
            ret = self.get_result()
        if event in self._events:
            self._events.remove(event)
            #print '  RELEASE', self._events
        return ret
    
    def set_cap_next(self):
        '''
        [CAP] << This in shown in GUI.
        
        Next emition will be uppercased.
        If it is already the case, toggle_caps_lock is used.
        '''
        v = True
        if self._cap_next:
            v = self.toggle_caps_lock()
        self._cap_next = v
    
    def toggle_caps_lock(self):
        '''
        Toggle 'all emitions are uppercased'.
        Return the new state as a boolean.
        '''
        self._caps_lock = not self._caps_lock
        return self._caps_lock
    
    def apply_cap(self, data, update_state=True):
        '''
        Return the caped version of data accordingly to
        the current caps options.
        If update_state is not True, the current caps options
        are left unchanged.
        '''
        if self._caps_lock:
            if isinstance(data, basestring):
                #result = result.upper()
                data = '+'+data
        elif self._cap_next:
            if isinstance(data, basestring):
                data = '+'+data
                if update_state:
                    self._cap_next = False
        return data